/*
 *  Copyright 2008-2010 www.jrails.org
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.rails.core.model.attribute;

import java.sql.SQLException;

import org.apache.log4j.Logger;

import org.rails.core.db.table.Table;
import org.rails.core.model.ActiveRecord;
import org.rails.core.resource.ConfigBuilder;
import org.rails.core.resource.ResourceFactory;
import org.rails.core.sql.SQLManager;
import org.rails.core.util.Utils;

public abstract class AttributeBase {

	protected final Logger logger = Logger.getLogger(this.getClass());

	protected final ConfigBuilder config = ResourceFactory.getConfigBuilder();

	protected final static String ATTRIBUTEBASE_MIN_ERROR_MESSAGE = "{0}.{1} = {2} 超出最小值限制!";
	protected final static String ATTRIBUTEBASE_MAX_ERROR_MESSAGE = "{0}.{1} = {2} 超出最大值限制!";
	protected final static String ATTRIBUTEBASE_TYPE_ERROR_MESSAGE = "{0}.{1} = {2} 数据类型不被接受!";
	protected final static String ATTRIBUTEBASE_FORMAT_ERROR_MESSAGE = "{0}.{1} = {2} 数据格式不被接受!";
	protected final static String ATTRIBUTEBASE_REQUIRE_ERROR_MESSAGE = "{0}.{1} = {2} 违反非空约束!";
	protected final static String ATTRIBUTEBASE_UNIQUE_ERROR_MESSAGE = "{0}.{1} = {2} 违反唯一约束!";

	public final static String ERROR_TYPE_MIN = "MIN";
	public final static String ERROR_TYPE_MAX = "MAX";
	public final static String ERROR_TYPE_TYPE = "TYPE";
	public final static String ERROR_TYPE_FORMAT = "FORMAT";
	public final static String ERROR_TYPE_REQUIRE = "REQUIRE";
	public final static String ERROR_TYPE_UNIQUE = "UNIQUE";

	protected final static String DEFAULT_KEY = "{0}.{1}.default";
	protected final static String REQUIRE_KEY = "{0}.{1}.require";
	protected final static String MIN_KEY = "{0}.{1}.min";
	protected final static String MAX_KEY = "{0}.{1}.max";
	protected final static String FORMAT_KEY = "{0}.{1}.format";
	protected final static String MESSAGE_KEY = "{0}.{1}.message";
	protected final static String TYPE_KEY = "{0}.{1}.type";
	protected final static String TRIM_KEY = "{0}.{1}.trim";
	protected final static String UNIQUE_KEY = "{0}.{1}.unique";
	protected final static String CONDITION_KEY = "{0}.{1}.condition";	
	protected final static String JOIN_KEY = "{0}.{1}.join";

	protected String modAtt;
	protected String modelName;
	protected String name;
	protected String format;
	protected String message;
	protected String type;
	protected String condition;
	protected String join;
	protected String exceMsg;
	protected String errorType;
	protected Object inputValue;

	protected boolean require = false;
	protected boolean unique = false;

	protected ActiveRecord record;

	public abstract void validate() throws AttributeException;

	public abstract Object getMin();

	public abstract Object getMax();

	public abstract Object getDefaultValue();

	public abstract Object parse(Object arg) throws AttributeException;

	public abstract String format(Object arg);

	public abstract Object getValue();

	public abstract boolean isTrim();

	public AttributeBase(final ActiveRecord record,
			final String name) {
		super();
		this.record = record;
		this.modelName = record.getClass().getSimpleName();
		this.name = name;
		init();
	}

	private void init() {
		inputValue = record.get(name);
		format = get(FORMAT_KEY);
		message = get(MESSAGE_KEY);
		type = get(TYPE_KEY);
		condition = get(CONDITION_KEY);
		join = get(JOIN_KEY);
		require = Boolean.parseBoolean(get(REQUIRE_KEY));
		unique = Boolean.parseBoolean(get(UNIQUE_KEY));
		modAtt = Utils.format("{0}.{1}", modelName, name);
	}
	
	public ActiveRecord getRecord(){
		return record;
	}

	public String getFormat() {
		return format;
	}

	public boolean isRequire() {
		return require;
	}

	public boolean isUnique() {
		return unique;
	}

	public String getCondition() {
		return condition;
	}

	public String getType() {
		return type;
	}

	public String getMessage() {
		return message;
	}

	protected String get(String key) {
		return config.get(getKey(key));
	}
	
	protected String getKey(String key) {
		return Utils.format(key, modelName, name);
	}

	protected boolean containsKey(String key) {
		return config.containsKey(getKey(key));
	}

	protected boolean isEmpty(Object arg) {
		return Utils.isEmpty(arg);
	}

	protected String errorMessage(String megKey) {
		return Utils.format(megKey, modelName, name,getInputValue());
	}
	
	public String getName(){
		return name;
	}
	
	public String getModel(){
		return this.modelName;
	}
	
	public void setExceMsg(String exceMsg){
		this.exceMsg = exceMsg;
	}
	
	public String getExceMsg(){
		return exceMsg;
	}

	public void setErrorType(String errorType){
		this.errorType = errorType;
	}
	
	public String getErrorType(){
		return errorType;
	}
	
	public Object getInputValue(){
		return inputValue;
	}
	
	protected void checkUnique() throws AttributeException {
		final SQLManager sql = new SQLManager(record);
		int c = 0;
		final Table t = sql.getTable();
		final String cond = Utils.format("{0}.{1} = :{2}", t.getFormatName(),t.formatColumn(name),name);
		final String col = Utils.format("COUNT({0}.{1}) AS c", t.getFormatName(), t.getFormatPrimaryKey());
		sql.clearColumns();
		sql.addColumn(col);
		sql.addCondition(cond);
		sql.addCondition(condition);
		sql.addJoin(join);
		sql.addParameter(record);
		sql.addParameter(name,getValue());
		
		if (!Utils.isEmpty(record.getId()))
			sql.addCondition(Utils.format("AND {0} <> :{0}",record.getTable().getPrimaryKey()));

		try {
			c = Integer.parseInt(record.findFirst(sql).get("c").toString());
			if (c > 0)
				throwAttUniqueError();

		} catch (SQLException e) {
			logger.error(e.getMessage(), e);
		}
	}
	
	protected void throwAttUniqueError() throws AttributeException{
		setErrorType(ERROR_TYPE_UNIQUE);
		throw new AttributeException(this,ERROR_TYPE_UNIQUE,errorMessage(ATTRIBUTEBASE_UNIQUE_ERROR_MESSAGE));
	}
	
	protected void throwAttTypeError() throws AttributeException{
		setErrorType(ERROR_TYPE_TYPE);
		throw new AttributeException(this,ERROR_TYPE_TYPE,errorMessage(ATTRIBUTEBASE_TYPE_ERROR_MESSAGE));
	}
	
	protected void throwAttFormatError() throws AttributeException{
		setErrorType(ERROR_TYPE_FORMAT);
		throw new AttributeException(this,ERROR_TYPE_FORMAT,errorMessage(ATTRIBUTEBASE_FORMAT_ERROR_MESSAGE));
	}
	
	protected void throwAttMaxError() throws AttributeException{
		setErrorType(ERROR_TYPE_MAX);
		throw new AttributeException(this, ERROR_TYPE_MAX,errorMessage(ATTRIBUTEBASE_MAX_ERROR_MESSAGE));
	}
	
	protected void throwAttMinError() throws AttributeException{
		setErrorType(ERROR_TYPE_MIN);
		throw new AttributeException(this,ERROR_TYPE_MIN,errorMessage(ATTRIBUTEBASE_MIN_ERROR_MESSAGE));		
	}
	
	protected void throwAttRequireError() throws AttributeException{
		setErrorType(ERROR_TYPE_REQUIRE);
		throw new AttributeException(this,ERROR_TYPE_REQUIRE,errorMessage(ATTRIBUTEBASE_REQUIRE_ERROR_MESSAGE));		
	}

}
